package pers.cz.thread;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pers.cz.mxgraph.MxCell;
import pers.cz.node.Node;
import pers.cz.node.NodeCallBack;
import pers.cz.node.NodeRunState;
import pers.cz.node.NodeTypeEnum;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

/**
 * @program: Reids
 * @description: 流程执行器
 * @author: Cheng Zhi
 * @create: 2023-01-09 10:52
 **/
public class AutoUiExcutor {

    Logger logger = LoggerFactory.getLogger(AutoUiExcutor.class);

    private static final String NODE_NAME = "nodeName";

    /**
     * 全局锁, 用于控制多个上游节点公用一个下游节点的情况，保证下游节点只运行一次
     */
    private CountDownLatch latch;

    public void start(MxCell mxCell,String folwId) {

        this.run(mxCell, folwId);

    }

    public void run(MxCell mxCell, String folwId) {

        Node node = getNodeInstance(mxCell);
        logger.debug(node.getName() + "即将运行" + Thread.currentThread().getName());
        node.run(new NodeCallBack() {
            @Override
            public void callBack(Map<String,Object> paramsMap) {
                // 执行下一个时先等待一下，防止初始化工作没有完成
                // 1标识成功，2标识执行失败
                if (paramsMap.get(NodeRunState.RUN_TYPE).equals(NodeRunState.RUN_SUCCESS)) {
                    runNext(mxCell.hasNext() ? mxCell.getNextCell() : new ArrayList<MxCell>(), paramsMap, folwId);
                }

            }
        });
    }

    public void runNext(List<MxCell> mxCells, Map<String,Object> paramsMap, String folwId) {

        // 串行
        int size = mxCells.size();
        if (size > 0 && size == 1) {
            MxCell mxCell = mxCells.get(0);
            mxCell.setParams(paramsMap); // 设置自定义参数
            if (!hasManyUpstream(mxCell, folwId)) {
                run(mxCell,folwId);
            }
            return;
        }

        // 多个下游节点需要并行运行
        for (MxCell mxCell : mxCells) {
            mxCell.setParams(paramsMap); // 设置自定义参数
            // 如果节点具有多个上游，要等待所有上游执行完成，再执行，且只执行一次
            if (!hasManyUpstream(mxCell, folwId)) {
                AutoUiThreadPool.getThreadPool(folwId).execute(() -> {
                    run(mxCell, folwId);
                });
            }
        }

        // 如果没有下游节点了，需要将线程池停止, 测试之后发现不能直接关闭掉，这样后面的线程会被拒绝
        /*if (mxCells.size() == 0) {
            AutoUiThreadPool.stopThreadPool(folwId);
        }*/
    }

    /**
     * 处理节点具有多个上游节点的情况。
     * @param mxCell
     * @param folwId
     * @return
     */
    private boolean hasManyUpstream(MxCell mxCell, String folwId) {
        if (mxCell.hasManyUpstreamMode()) {
            List<Integer> upstreamNode = mxCell.getUpstreamNode();
            logger.debug(mxCell.getName() + "具有多个上游节点,节点个数为：" + upstreamNode.size());
            if (latch == null || latch.getCount() == 0) {
                latch = new CountDownLatch(upstreamNode.size());
            }

            latch.countDown();
            long count = latch.getCount();
            if (count == 0) {
                run(mxCell, folwId);
                // 运行过之后，就要把锁消耗完
                while(latch.getCount() > 0) {
                    latch.countDown();
                }
            }

            /*AutoUiThreadPool.getThreadPool(folwId).execute(() -> {
                latch.countDown();
                long count = latch.getCount();
                if (count == 0) {
                    run(mxCell, folwId);
                    // 运行过之后，就要把锁消耗完
                    while(latch.getCount() > 0) {
                        latch.countDown();
                    }
                }
            });*/

            return true;
        }

        return false;
    }

    private Node getNodeInstance(MxCell mxCell) {

        String name = mxCell.getMxObjectData().get(NODE_NAME);
        Class<? extends Node> nodeClassByNodeName = NodeTypeEnum.getNodeClassByNodeName(name);
        Node node = null;
        try {
            node = nodeClassByNodeName.getConstructor(MxCell.class).newInstance(mxCell);
        } catch (InstantiationException | InvocationTargetException e) {
            logger.error("reflect error", e);
        } catch (IllegalAccessException | NoSuchMethodException e) {
            logger.error("reflect error", e);
        }

        return node;
    }
}
